home *** CD-ROM | disk | FTP | other *** search
/ Apple Developer Connection Student Program / ADC Tools Sampler CD Disk 3 1999.iso / Metrowerks CodeWarrior / Java Support / Java_Source / Java2 / src / java / io / ObjectOutputStream.java < prev    next >
Encoding:
Java Source  |  1999-05-28  |  60.7 KB  |  1,941 lines  |  [TEXT/CWIE]

  1. /*
  2.  * @(#)ObjectOutputStream.java    1.70 98/04/17
  3.  *
  4.  * Copyright 1996-1998 by Sun Microsystems, Inc.,
  5.  * 901 San Antonio Road, Palo Alto, California, 94303, U.S.A.
  6.  * All rights reserved.
  7.  *
  8.  * This software is the confidential and proprietary information
  9.  * of Sun Microsystems, Inc. ("Confidential Information").  You
  10.  * shall not disclose such Confidential Information and shall use
  11.  * it only in accordance with the terms of the license agreement
  12.  * you entered into with Sun.
  13.  */
  14.  
  15. package java.io;
  16.  
  17. import java.lang.reflect.InvocationTargetException;
  18. import java.util.ArrayList;
  19. import java.util.Arrays;
  20. import java.lang.reflect.InvocationTargetException;
  21. import java.security.AccessController;
  22.  
  23. /**
  24.  * An ObjectOutputStream writes primitive data types and graphs of
  25.  * Java objects to an OutputStream.  The objects can be read
  26.  * (reconstituted) using an ObjectInputStream.
  27.  * Persistent storage of objects can be accomplished by using a file for
  28.  * the stream.
  29.  * If the stream is a network socket stream, the objects can be reconsituted
  30.  * on another host or in another process. <p>
  31.  *
  32.  * Only objects that support the java.io.Serializable interface can be
  33.  * written to streams.
  34.  *
  35.  * The class of each serializable object is encoded including the class
  36.  * name and signature of the class, the values of the
  37.  * object's fields and arrays, and the closure of any other objects
  38.  * referenced from the initial objects. <p>
  39.  *
  40.  * The method <STRONG>writeObject</STRONG> is used to write an object
  41.  * to the stream.  Any object, including Strings and arrays, is
  42.  * written with writeObject. Multiple objects or primitives can be
  43.  * written to the stream.  The objects must be read back from the
  44.  * corresponding ObjectInputstream with the same types and in the same
  45.  * order as they were written.<p>
  46.  *
  47.  * Primitive data types can also be written to the stream using the
  48.  * appropriate methods from DataOutput. Strings can also be written
  49.  * using the writeUTF method.<p>
  50.  *
  51.  * The default serialization mechanism for an object writes the class
  52.  * of the object, the class signature, and the values of all
  53.  * non-transient and non-static fields.  References to other objects
  54.  * (except in transient or static fields) cause those objects to be
  55.  * written also. Multiple references to a single object are encoded
  56.  * using a reference sharing mechanism so that graphs of objects can
  57.  * be restored to the same shape as when the original was written. <p>
  58.  *
  59.  * For example to write an object that can be read by the example in ObjectInputStream: <br>
  60.  * <PRE>
  61.  *    FileOutputStream ostream = new FileOutputStream("t.tmp");
  62.  *    ObjectOutputStream p = new ObjectOutputStream(ostream);
  63.  *
  64.  *    p.writeInt(12345);
  65.  *    p.writeObject("Today");
  66.  *    p.writeObject(new Date());
  67.  *
  68.  *    p.flush();
  69.  *    ostream.close();
  70.  *
  71.  * </PRE>
  72.  *
  73.  * Classes that require special handling during the serialization and deserialization
  74.  * process must implement special methods with these exact signatures: <p>
  75.  *
  76.  * <PRE>
  77.  * private void readObject(java.io.ObjectInputStream stream)
  78.  *     throws IOException, ClassNotFoundException;
  79.  * private void writeObject(java.io.ObjectOutputStream stream)
  80.  *     throws IOException
  81.  * </PRE><p>
  82.  * The writeObject method is responsible for writing the state of
  83.  * the object for its particular class so that the corresponding
  84.  * readObject method can restore it.
  85.  * The method does not need to concern itself with the
  86.  * state belonging to the object's superclasses or subclasses.
  87.  * State is saved by writing the individual fields to the ObjectOutputStream
  88.  * using the writeObject method or by using the methods for
  89.  * primitive data types supported by DataOutput. <p>
  90.  *
  91.  * Serialization does not write out the fields of any object that does
  92.  * not implement the java.io.Serializable interface.  Subclasses of
  93.  * Objects that are not serializable can be serializable. In this case
  94.  * the non-serializable class must have a no-arg constructor to allow
  95.  * its fields to be initialized.  In this case it is the
  96.  * responsibility of the subclass to save and restore the state of the
  97.  * non-serializable class. It is frequently the case that the fields
  98.  * of that class are accessible (public, package, or protected) or
  99.  * that there are get and set methods that can be used to restore the
  100.  * state. <p>
  101.  *
  102.  * Serialization of an object can be prevented by implementing writeObject
  103.  * and readObject methods that throw the NotSerializableException.
  104.  * The exception will be caught by the ObjectOutputStream and abort the
  105.  * serialization process.
  106.  *
  107.  * Implementing the Externalizable interface allows the object to
  108.  * assume complete control over the contents and format of the object's
  109.  * serialized form.  The methods of the Externalizable interface,
  110.  * writeExternal and readExternal, are called to save and restore the
  111.  * objects state.  When implemented by a class they can write and read
  112.  * their own state using all of the methods of ObjectOutput and
  113.  * ObjectInput.  It is the responsibility of the objects to handle any
  114.  * versioning that occurs.
  115.  *
  116.  * Primitive data, excluding serializable fields and externalizable
  117.  * data, is written to the ObjectOutputStream in block-data
  118.  * records. A block data record is composed of a header and
  119.  * data. The block data header consists of a marker and the
  120.  * number of bytes to follow the header.  Consecutive primitive data
  121.  * writes are merged into one block-data record.
  122.  *
  123.  *  (*) The blocking factor used for a block-data record will
  124.  *      be 1024 bytes.
  125.  *
  126.  *  (*) Each block-data record will be filled up to 1024 bytes, or be
  127.  *      written whenever there is a termination of block-data mode.
  128.  *
  129.  *  Calls to the ObjectOutputStream methods writeObject,
  130.  *  defaultWriteObject and writeFields initially terminate any
  131.  *  existing block-data record.
  132.  *
  133.  * @author    Roger Riggs
  134.  * @version     1.67, 03/02/98
  135.  * @see java.io.DataOutput
  136.  * @see java.io.ObjectInputStream
  137.  * @see java.io.Serializable
  138.  * @see java.io.Externalizable
  139.  * @see <a href="http://java.sun.com/products/jdk/1.2/docs/guide/serialization/spec/output.doc.html"> Object Serialization Specification, Section 2, Object Output Classes</a>
  140.  * @since       JDK1.1
  141.  */
  142. public class ObjectOutputStream
  143.     extends OutputStream
  144.     implements ObjectOutput, ObjectStreamConstants
  145.  
  146. {
  147.     /**
  148.      * Creates an ObjectOutputStream that writes to the specified OutputStream.
  149.      * The stream header is written to the stream. The caller may want to call
  150.      * flush immediately so that the corresponding ObjectInputStream can read
  151.      * the header immediately.
  152.      *
  153.      * @exception IOException Any exception thrown by the underlying OutputStream.
  154.  
  155.      */
  156.     public ObjectOutputStream(OutputStream out) throws IOException {
  157.     enableSubclassImplementation = false;
  158.     this.out = out;
  159.     dos = new DataOutputStream(this);
  160.     buf = new byte[1024];    // allocate buffer
  161.     writeStreamHeader();
  162.     resetStream();
  163.     }
  164.  
  165.     /**
  166.      * Provide a way for subclasses that are completely reimplementing
  167.      * ObjectOutputStream to not have to allocate private data just used by
  168.      * this implementation of ObjectOutputStream.
  169.      *
  170.      * <p>If there is a security manager installed, this method first calls the
  171.      * security manager's <code>checkPermission</code> method with a
  172.      * <code>SerializablePermission("enableSubclassImplementation")</code>
  173.      * permission to ensure it's ok to enable subclassing.
  174.      *
  175.      * @exception IOException   Thrown if not called by a subclass.
  176.      * 
  177.      * @throws SecurityException
  178.      *    if a security manager exists and its 
  179.      *    <code>checkPermission</code> method denies
  180.      *    enabling subclassing.
  181.      *
  182.      * @see SecurityManager#checkPermission
  183.      * @see java.security.SerializablePermission
  184.      */
  185.     protected ObjectOutputStream() throws IOException, SecurityException {
  186.     SecurityManager sm = System.getSecurityManager();
  187.     if (sm != null) sm.checkPermission(SUBCLASS_IMPLEMENTATION_PERMISSION);
  188.     enableSubclassImplementation = true;
  189.      }
  190.  
  191.     /**
  192.      * This method is called by trusted subclasses of ObjectInputStream
  193.      * that constructed ObjectInputStream using the
  194.      * protected no-arg constructor. The subclass is expected to provide
  195.      * an override method with the modifier "final".
  196.      *
  197.      * @see #ObjectOutputStream()
  198.      * @see #writeObject(Object)
  199.      * @since JDK 1.2
  200.      */
  201.     protected void writeObjectOverride(Object obj) throws IOException
  202.     {
  203.     }
  204.  
  205.     /**
  206.      * Specify stream protocol version to use when writing the stream.<p>
  207.      *
  208.      * This routine provides a hook to enable the current version
  209.      * of Serialization to write in a format that is backwards
  210.      * compatible to a previous version of the stream format.<p>
  211.      *
  212.      * Every effort will be made to avoid introducing additional
  213.      * backwards incompatibilities; however, sometimes there is no
  214.      * other alternative.<p>
  215.      *
  216.      * @param version   use ProtocolVersion from java.io.ObjectStreamConstants.
  217.      * @exception IllegalStateException   Thrown if called after any objects have
  218.      *                          been serialized.
  219.      * @exception IllegalArgument if invalid version is passed in.
  220.      *
  221.      * @see java.io.ObjectStreamConstants#PROTOCOL_VERSION_1
  222.      * @see java.io.ObjectStreamConstants#PROTOCOL_VERSION_2
  223.      */
  224.     public void useProtocolVersion(int version) throws IOException {
  225.     if (nextWireOffset != 0)
  226.         throw new IllegalStateException("Must call useProtocolVersion before writing any objects to the stream");
  227.  
  228.     switch (version) {
  229.     case PROTOCOL_VERSION_1:
  230.         useDeprecatedExternalizableFormat = true;
  231.         break;
  232.     case PROTOCOL_VERSION_2:
  233.         break;
  234.     default:
  235.         throw new IllegalArgumentException("unknown version:" + version);
  236.     };
  237.     }
  238.  
  239.     /**
  240.      * Write the specified object to the ObjectOutputStream.
  241.      * The class of the object, the signature of the class, and the values
  242.      * of the non-transient and non-static fields of the class and all
  243.      * of its supertypes are written.  Default serialization for a class can be
  244.      * overridden using the writeObject and the readObject methods.
  245.      * Objects referenced by this object are written transitively so
  246.      * that a complete equivalent graph of objects can be
  247.      * reconstructed by an ObjectInputStream.  <p>
  248.      *
  249.      * Exceptions are thrown for
  250.      * problems with the OutputStream and for classes that should not be
  251.      * serialized.  All exceptions are fatal to the OutputStream, which
  252.      * is left in an indeterminate state, and it is up to the caller
  253.      * to ignore or recover the stream state.
  254.      * @exception InvalidClassException Something is wrong with a class used by
  255.      *       serialization.
  256.      * @exception NotSerializableException Some object to be serialized does not
  257.      *      implement the java.io.Serializable interface.
  258.      * @exception IOException Any exception thrown by the underlying OutputStream.
  259.      */
  260.     public final void writeObject(Object obj)
  261.     throws IOException
  262.     {
  263.     if (enableSubclassImplementation) {
  264.         writeObjectOverride(obj);
  265.         return;
  266.     }
  267.  
  268.     Object prevObject = currentObject;
  269.     ObjectStreamClass prevClassDesc = currentClassDesc;
  270.     boolean oldBlockDataMode = setBlockData(false);
  271.     recursionDepth++;
  272.  
  273.     try {
  274.         if (serializeNullAndRepeat(obj, REPLACEABLE))
  275.         return;
  276.  
  277.         if (checkSpecialClasses(obj))
  278.         return;
  279.  
  280.         currentClassDesc =
  281.         ObjectStreamClass.lookupInternal(obj.getClass());
  282.  
  283.         Object altobj = obj;
  284.  
  285.         /* Allow the class to replace the instance to be serialized. */
  286.         if (currentClassDesc.isReplaceable()) {
  287.         altobj =
  288.             ObjectStreamClass.invokeMethod(currentClassDesc.writeReplaceMethod,
  289.                            obj,
  290.                            null);
  291.         }
  292.  
  293.         /* If the replacment is enabled, give subclasses one chance
  294.          * to substitute a new object. If one is substituted,
  295.          * recheck for null, repeated refs, and special cased classes
  296.          */
  297.         if (enableReplace) {
  298.         altobj = replaceObject(altobj);
  299.         }
  300.  
  301.         /* If the object has been replaced check that the object
  302.          * is serializable and recheck for the special cases.
  303.          */
  304.         if (obj != altobj) {
  305.         currentClassDesc = altobj == null ? 
  306.             null : 
  307.             ObjectStreamClass.lookupInternal(altobj.getClass());
  308.         if (altobj != null && ! (altobj instanceof Serializable)) {
  309.             String clname = altobj.getClass().getName();
  310.             throw new NotSerializableException(clname);
  311.         }
  312.  
  313.         // If the alternate object is already
  314.         // serialized just remember the replacement
  315.         if (serializeNullAndRepeat(altobj, REPLACEABLE)) {
  316.             addReplacement(obj, altobj);
  317.             return;
  318.         }
  319.  
  320.         /* Add this to the set of replaced objects.
  321.          * This must be done before the object is
  322.          * serialized so that if the object indirectly
  323.          * refers to the original it will be redirected to
  324.          * the replacement.
  325.          *
  326.          * NB: checkSpecialClasses should only call
  327.          * serializeNullandRepeat for objects that will not
  328.          * recurse.
  329.          */
  330.         addReplacement(obj, altobj);
  331.  
  332.         if (checkSpecialClasses(altobj))
  333.             return;
  334.  
  335.         obj = altobj;
  336.         }
  337.         if (checkSubstitutableSpecialClasses(obj,
  338.                          currentClassDesc.forClass()))
  339.         return;
  340.  
  341.         /* Write out the object as itself */
  342.         outputObject(obj);
  343.     } catch (IOException ee) {
  344.         if (abortIOException == null) {
  345.         try {
  346.             /* Prepare to write the exception to the stream.
  347.              * End blockdatamode in case it's set
  348.              * Write the exception code
  349.              * reset the stream to forget all previous objects
  350.              * write the exception that occurred
  351.              * reset the stream again so subsequent objects won't map to
  352.              * the exception or its args.
  353.              * Continue below to rethrow the exception.
  354.              */
  355.             setBlockData(false);
  356.  
  357.             writeCode(TC_EXCEPTION);
  358.             resetStream();
  359.             setBlockData(false); //added since resetStream set to TRUE.
  360.             currentClassDesc =
  361.             ObjectStreamClass.lookupInternal(ee.getClass());
  362.             this.outputObject(ee); //avoid recursing with writeObject
  363.             resetStream();
  364.  
  365.             // Set the pending exception to be rethrown.
  366.             abortIOException = ee;
  367.         } catch (IOException fatal) {
  368.             /* An exception occurred while writing the original exception to
  369.              * the stream.  The original exception is not complete in
  370.              * the stream and recusion would be bad. Supercede the original
  371.              * Exception with a StreamCorruptedException using the message
  372.              * from this current exception.
  373.              */
  374.             abortIOException =
  375.             new StreamCorruptedException(fatal.getMessage());
  376.         }
  377.         }
  378.     } finally {
  379.         /* Restore state of previous call incase this is a nested call */
  380.         recursionDepth--;
  381.         currentObject = prevObject;
  382.         currentClassDesc = prevClassDesc;
  383.         setBlockData(oldBlockDataMode);
  384.     }
  385.  
  386.     /* If the recursion depth is 0, test for and clear the pending exception.
  387.      * If there is a pending exception throw it.
  388.      */
  389.     IOException pending = abortIOException;
  390.     if (recursionDepth == 0)
  391.         abortIOException = null;
  392.     if (pending != null) {
  393.         throw pending;
  394.     }
  395.     }
  396.  
  397.     /*
  398.      * Check for special cases of serializing objects.
  399.      * These objects are not subject to replacement.
  400.      */
  401.     private boolean checkSpecialClasses(Object obj) throws IOException {
  402.  
  403.     /*
  404.      * If this is a class, don't allow substitution
  405.      */
  406.     if (obj instanceof Class) {
  407.         outputClass((Class)obj);
  408.         return true;
  409.     }
  410.  
  411.     if (obj instanceof ObjectStreamClass) {
  412.         outputClassDescriptor((ObjectStreamClass)obj);
  413.         return true;
  414.     }
  415.  
  416.     return false;
  417.     }
  418.  
  419.     /*
  420.      * Check for special cases of substitutable serializing objects.
  421.      * These classes are replaceable.
  422.      */
  423.     private boolean checkSubstitutableSpecialClasses(Object obj, Class cl)
  424.     throws IOException
  425.     {
  426.     if (cl == String.class) {
  427.         outputString((String)obj);
  428.         return true;
  429.     }
  430.  
  431.     if (cl.isArray()) {
  432.         outputArray(obj);
  433.         return true;
  434.     }
  435.  
  436.     return false;
  437.     }
  438.  
  439.     /**
  440.      * Write the non-static and non-transient fields of the current class
  441.      * to this stream.  This may only be called from the writeObject method
  442.      * of the class being serialized. It will throw the NotActiveException
  443.      * if it is called otherwise.
  444.      */
  445.     public void defaultWriteObject() throws IOException {
  446.     if (currentObject == null || currentClassDesc == null)
  447.         throw new NotActiveException("defaultWriteObject");
  448.  
  449.     ObjectStreamField[] fields =
  450.         currentClassDesc.getFieldsNoCopy();
  451.     if (fields.length > 0) {
  452.         boolean prevmode = setBlockData(false);
  453.         outputClassFields(currentObject, currentClassDesc.forClass(),
  454.                   fields);
  455.         setBlockData(prevmode);
  456.     }
  457.     }
  458.  
  459.     /**
  460.      * Retrieve the object used to buffer persistent fields to be written to
  461.      * the stream.  The fields will be written to the stream when writeFields
  462.      * method is called.
  463.      *
  464.      * @since JDK1.2
  465.      */
  466.     public ObjectOutputStream.PutField putFields() throws IOException {
  467.     if (currentObject == null || currentClassDesc == null)
  468.         throw new NotActiveException("putFields");
  469.         // TBD: check if defaultWriteObject has already been called.
  470.         currentPutFields = new ObjectOutputStream.PutFieldImpl(currentClassDesc);
  471.         return currentPutFields;
  472.     }
  473.  
  474.     /**
  475.      * Write the buffered fields to the stream.
  476.      *
  477.      * @since JDK1.2
  478.      * @exception NotActiveException Called when a classes writeObject
  479.      * method was not called to write the state of the object.
  480.      */
  481.     public void writeFields() throws IOException {
  482.         if (currentObject == null || currentClassDesc == null || currentPutFields == null)
  483.             throw new NotActiveException("writeFields");
  484.  
  485.     boolean prevmode = setBlockData(false);
  486.         currentPutFields.write(this);
  487.     setBlockData(prevmode);
  488.     }
  489.  
  490.     /**
  491.      * Reset will disregard the state of any objects already written
  492.      * to the stream.  The state is reset to be the same as a new
  493.      * ObjectOutputStream.  The current point in the stream is marked
  494.      * as reset so the corresponding ObjectInputStream will be reset
  495.      * at the same point.  Objects previously written to the stream
  496.      * will not be refered to as already being in the stream.  They
  497.      * will be written to the stream again.
  498.      */
  499.     public void reset() throws IOException {
  500.     if (currentObject != null || currentClassDesc != null)
  501.         throw new IOException("Illegal call to reset");
  502.  
  503.     /* Write a reset to the stream. */
  504.     setBlockData(false);
  505.     writeCode(TC_RESET);
  506.  
  507.     resetStream();            // re-init the stream
  508.     abortIOException = null;
  509.     }
  510.  
  511.     /*
  512.      * Internal reset function to reinitialize the state of the stream.
  513.      * Reset state of things changed by using the stream.
  514.      */
  515.     private void resetStream() throws IOException {
  516.     if (wireHandle2Object == null) {
  517.         wireHandle2Object = new ArrayList();
  518.         wireNextHandle = new int[4];
  519.         wireHash2Handle = new int[ (1 << wireHashSizePower) - 1];
  520.     } else {
  521.  
  522.         // Storage Optimization for frequent calls to reset method.
  523.         // Do not reallocate, only reinitialize.
  524.         wireHandle2Object.clear();
  525.         for (int i = 0; i < nextWireOffset; i++) {
  526.         wireNextHandle[i] = 0;
  527.         }
  528.     }
  529.       nextWireOffset = 0;
  530.     Arrays.fill(wireHash2Handle, -1);
  531.  
  532.     if (classDescStack == null)
  533.         classDescStack = new Stack();
  534.     else
  535.         classDescStack.setSize(0);
  536.  
  537.     for (int i = 0; i < nextReplaceOffset; i++)
  538.         replaceObjects[i] = null;
  539.     nextReplaceOffset = 0;
  540.  
  541.       setBlockData(true);        /* Re-enable buffering */
  542.       }
  543.  
  544.     /**
  545.      * Subclasses may implement this method to allow class data to be stored
  546.      * in the stream. By default this method does nothing.
  547.      * The corresponding method in ObjectInputStream is resolveClass.
  548.      * This method is called exactly once for each unique class in the stream.
  549.      * The class name and signature will have already been written to the stream.
  550.      * This method may make free use of the ObjectOutputStream to save
  551.      * any representation of the class it deems suitable (for example,
  552.      * the bytes of the class file).  The resolveClass method in the corresponding
  553.      * subclass of ObjectInputStream must read and use any data or objects
  554.      * written by annotateClass.
  555.      *
  556.      * @exception IOException Any exception thrown by the underlying OutputStream.
  557.      */
  558.     protected void annotateClass(Class cl)
  559.     throws IOException
  560.     {
  561.     }
  562.  
  563.     /** This method will allow trusted subclasses of ObjectOutputStream
  564.      * to substitute one object for another during
  565.      * serialization. Replacing objects is disabled until
  566.      * enableReplaceObject is called. The enableReplaceObject method
  567.      * checks that the stream requesting to do replacment can be
  568.      * trusted. Every reference to serializable objects is passed to
  569.      * replaceObject.  To insure that the private state of objects is
  570.      * not unintentionally exposed only trusted streams may use
  571.      * replaceObject. <p>
  572.      *
  573.      * When a subclass is replacing objects it must insure that either
  574.      * a complementary substitution must be made during
  575.      * deserialization or that the substituted object is compatible
  576.      * with every field where the reference will be stored.  Objects
  577.      * whose type is not a subclass of the type of the field or array
  578.      * element abort the serialization by raising an exception and the
  579.      * object is not be stored. <p>
  580.      *
  581.      * This method is called only once when each object is first encountered.
  582.      * All subsequent references to the object will be redirected to the
  583.      * new object. This method should return the object to be substituted or
  584.      * the original object. <P>
  585.      *
  586.      * Null can be returned as the object to be substituted, but may
  587.      * cause NullReferenceException in classes that contain references
  588.      * to the original object since they may be expecting an object
  589.      * instead of null.<p>
  590.      *
  591.      * @exception IOException Any exception thrown by the underlying
  592.      * OutputStream.
  593.      */
  594.     protected Object replaceObject(Object obj)
  595.     throws IOException
  596.     {
  597.     return obj;
  598.     }
  599.  
  600.     /**
  601.      * Enable the stream to do replacement of objects in the stream.
  602.      *
  603.      * <p>When enabled, the replaceObject method is called for every object
  604.      * being serialized.
  605.      *
  606.      * <p>If <i>enable</i> is true, and there is a security manager installed,
  607.      * this method first calls the
  608.      * security manager's <code>checkPermission</code> method with a
  609.      * <code>SerializablePermission("enableSubstitution")</code>
  610.      * permission to ensure it's ok to 
  611.      * enable the stream to do replacement of objects in the stream.
  612.      *
  613.      * @throws SecurityException
  614.      *    if a security manager exists and its 
  615.      *    <code>checkPermission</code> method denies
  616.      *    enabling the stream to do replacement of objects in the stream.
  617.      *
  618.      * @see SecurityManager#checkPermission
  619.      * @see java.security.SerializablePermission
  620.      */
  621.     protected boolean enableReplaceObject(boolean enable)
  622.     throws SecurityException
  623.     {
  624.     boolean previous = enableReplace;
  625.     if (enable) {
  626.         SecurityManager sm = System.getSecurityManager();
  627.         if (sm != null) sm.checkPermission(SUBSTITUTION_PERMISSION);
  628.          enableReplace = true;
  629.     } else {
  630.         enableReplace = false;
  631.     }
  632.     return previous;
  633.     }
  634.  
  635.     /**
  636.      * The writeStreamHeader method is provided so subclasses can
  637.      * append or prepend their own header to the stream.
  638.      * It writes the magic number and version to the stream.
  639.      */
  640.     protected void writeStreamHeader() throws IOException {
  641.     writeShort(STREAM_MAGIC);
  642.     writeShort(STREAM_VERSION);
  643.     }
  644.  
  645.     /**
  646.      * Write a string to the stream.
  647.      * Note that since Strings are Objects, writeObject
  648.      * will behave identically.
  649.      */
  650.     private void outputString(String s) throws IOException {
  651.     /* Allocate a write handle but don't write it to the stream,
  652.      * Write out the code for a string,
  653.      * the read can regenerate the same sequence and it saves bytes.
  654.      */
  655.     assignWireOffset(s);
  656.     writeCode(TC_STRING);
  657.     writeUTF(s);
  658.     }
  659.  
  660.  
  661.     /* Classes are special, they can not be created during deserialization,
  662.      * but the appropriate class can be found.
  663.      */
  664.     private void outputClass(Class aclass) throws IOException {
  665.  
  666.     writeCode(TC_CLASS);
  667.     /* Find the class descriptor and write it out */
  668.     ObjectStreamClass v = ObjectStreamClass.lookupInternal(aclass);
  669.     if (v == null)
  670.         throw new NotSerializableException(aclass.getName());
  671.  
  672.     outputClassDescriptor(v);
  673.  
  674.     assignWireOffset(aclass);
  675.     }
  676.  
  677.  
  678.     /* Write the class descriptor */
  679.     private void outputClassDescriptor(ObjectStreamClass classdesc)
  680.     throws IOException
  681.     {
  682.     if (serializeNullAndRepeat(classdesc, NOT_REPLACEABLE))
  683.         return;
  684.  
  685.     /* Write out the code for a class
  686.      * Write out the class name and its serialVersionUID
  687.      */
  688.     writeCode(TC_CLASSDESC);
  689.     String classname = classdesc.getName();
  690.  
  691.     writeUTF(classname);
  692.     writeLong(classdesc.getSerialVersionUID());
  693.  
  694.     /* This is done here to be symetric with the inputClass method
  695.      * Since the resolveClassName() method may use the stream.
  696.      * The assignments of wirehandles must be done in the same order
  697.      */
  698.     assignWireOffset(classdesc);
  699.  
  700.     /* Write the version description for this class */
  701.     classdesc.write(this);
  702.  
  703.     /* Give subclassers a chance to add the class implementation
  704.      * to the stream.  Set BlockData mode so any information they
  705.      * write can be skipped on reading.
  706.      */
  707.     boolean prevMode = setBlockData(true);
  708.     annotateClass(classdesc.forClass());
  709.     setBlockData(prevMode);
  710.     writeCode(TC_ENDBLOCKDATA);
  711.  
  712.     /*
  713.      * Write out the superclass descriptor of this descriptor
  714.      * only if it is for a java.io.Serializable class.
  715.      * else write null.
  716.      */
  717.     ObjectStreamClass superdesc = classdesc.getSuperclass();
  718.     outputClassDescriptor(superdesc);
  719.     }
  720.  
  721.     /**
  722.      * Write an array out. Note that since Arrays are Objects, writeObject(obj)
  723.      * will behave identically. <br><br>
  724.      * @param o can represent an array of any type/dimension.
  725.      */
  726.     private void outputArray(Object obj)
  727.     throws IOException
  728.     {
  729.     Class currclass = currentClassDesc.forClass();
  730.  
  731.     /* Write out the code for an array and the name of the class */
  732.     writeCode(TC_ARRAY);
  733.     outputClassDescriptor(currentClassDesc);
  734.  
  735.     /* Assign the wirehandle for this object and outputArrayValues
  736.      * writes the length and the array contents.
  737.      */
  738.     assignWireOffset(obj);
  739.  
  740.     int i, length;
  741.     Class type = currclass.getComponentType();
  742.  
  743.     if (type.isPrimitive()) {
  744.         /* Write arrays of primitive types using the DataOutput
  745.          * methods that convert each element into the output buffer.
  746.          * The data types are ordered by the frequency
  747.          * in which they are expected to occur.
  748.          */
  749.         if (type == Integer.TYPE) {
  750.         int[] array = (int[])obj;
  751.         length = array.length;
  752.         writeInt(length);
  753.         for (i = 0; i < length; i++) {
  754.             writeInt(array[i]);
  755.         }
  756.         } else if (type == Byte.TYPE) {
  757.         byte[] array = (byte[])obj;
  758.         length = array.length;
  759.         writeInt(length);
  760.         writeInternal(array, 0, length, true);
  761.         } else if (type == Long.TYPE) {
  762.         long[] array = (long[])obj;
  763.         length = array.length;
  764.         writeInt(length);
  765.         for (i = 0; i < length; i++) {
  766.             writeLong(array[i]);
  767.         }
  768.         } else if (type == Float.TYPE) {
  769.         float[] array = (float[])obj;
  770.         length = array.length;
  771.         writeInt(length);
  772.         for (i = 0; i < length; i++) {
  773.             writeFloat(array[i]);
  774.         }
  775.         } else if (type == Double.TYPE) {
  776.         double[] array = (double[])obj;
  777.         length = array.length;
  778.         writeInt(length);
  779.         for (i = 0; i < length; i++) {
  780.             writeDouble(array[i]);
  781.         }
  782.         } else if (type == Short.TYPE) {
  783.         short[] array = (short[])obj;
  784.         length = array.length;
  785.         writeInt(length);
  786.         for (i = 0; i < length; i++) {
  787.             writeShort(array[i]);
  788.         }
  789.         } else if (type == Character.TYPE) {
  790.         char[] array = (char[])obj;
  791.         length = array.length;
  792.         writeInt(length);
  793.         for (i = 0; i < length; i++) {
  794.             writeChar(array[i]);
  795.         }
  796.         } else if (type == Boolean.TYPE) {
  797.         boolean[] array = (boolean[])obj;
  798.         length = array.length;
  799.         writeInt(length);
  800.         for (i = 0; i < length; i++) {
  801.             writeBoolean(array[i]);
  802.         }
  803.         } else {
  804.         throw new InvalidClassException(currclass.getName());
  805.         }
  806.     } else {
  807.         Object[] array = (Object[])obj;
  808.         length = array.length;
  809.         writeInt(length);
  810.         for (i = 0; i < length; i++) {
  811.         writeObject(array[i]);
  812.         }
  813.     }
  814.     }
  815.  
  816.     /*
  817.      * Write a typeString to the stream.
  818.      * Do not allow replaceObject to be called on typeString.
  819.      */
  820.     void writeTypeString(String typeString) throws IOException {
  821.  
  822.     int handle = findWireOffset(typeString);
  823.     if (handle >= 0) {
  824.         writeCode(TC_REFERENCE);
  825.         writeInt(handle + baseWireHandle);
  826.     } else {
  827.         assignWireOffset(typeString);
  828.         writeCode(TC_STRING);
  829.         writeUTF(typeString);
  830.     }
  831.     }
  832.  
  833.     /*
  834.      * Put the object into the stream The newObject code is written
  835.      * followed by the ObjectStreamClass for the object's class.  Each
  836.      * of the objects classes is written using the default
  837.      * serialization code and dispatching to Specials where
  838.      * appropriate.
  839.      */
  840.     private void outputObject(Object obj)
  841.     throws IOException
  842.     {
  843.     currentObject = obj;
  844.     if (currentClassDesc.isNonSerializable()) {
  845.         throw new NotSerializableException(currentClassDesc.getName());
  846.     }
  847.  
  848.  
  849.     /* Write the code to expect an instance and
  850.      * the class descriptor of the instance
  851.      */
  852.     writeCode(TC_OBJECT);
  853.     outputClassDescriptor(currentClassDesc);
  854.  
  855.     /* Assign the next wirehandle */
  856.     assignWireOffset(obj);
  857.  
  858.     /* If the object is externalizable,
  859.      * call writeExternal.
  860.      * else do Serializable processing.
  861.      */
  862.     if (currentClassDesc.isExternalizable()) {
  863.         Externalizable ext = (Externalizable)obj;
  864.         if (useDeprecatedExternalizableFormat) {
  865.  
  866.         /* JDK 1.1 external data format.
  867.          * Don't write in block data mode and no terminator tag.
  868.          */
  869.         ext.writeExternal(this);
  870.         } else {
  871.  
  872.         /* JDK 1.2 Externalizable data format writes in block data mode
  873.          * and terminates externalizable data with TAG_ENDBLOCKDATA.
  874.          */
  875.         setBlockData(true);
  876.         try {
  877.             ext.writeExternal(this);
  878.         } finally {
  879.             setBlockData(false);
  880.             writeCode(TC_ENDBLOCKDATA);
  881.         }
  882.         }
  883.     } else {
  884.  
  885.         /* The object's classes should be processed from supertype to subtype
  886.          * Push all the clases of the current object onto a stack.
  887.          * Remember the stack pointer where this set of classes is being pushed.
  888.          */
  889.         int stackMark = classDescStack.size();
  890.         try {
  891.         ObjectStreamClass next;
  892.         while ((next = currentClassDesc.getSuperclass()) != null) {
  893.             classDescStack.push(currentClassDesc);
  894.             currentClassDesc = next;
  895.         }
  896.  
  897.         /*
  898.          * For currentClassDesc and all the pushed class descriptors
  899.          *    If the class is writing its own data
  900.          *          set blockData = true; call the class writeObject method
  901.          *    If not
  902.          *     invoke either the defaultWriteObject method.
  903.          */
  904.         do {
  905.             if (currentClassDesc.hasWriteObject()) {
  906.             setBlockData(true); /* Block any data the class writes */
  907.             invokeObjectWriter(obj);
  908.             setBlockData(false);
  909.             writeCode(TC_ENDBLOCKDATA);
  910.             } else {
  911.             defaultWriteObject();
  912.             }
  913.         } while (classDescStack.size() > stackMark &&
  914.              (currentClassDesc = (ObjectStreamClass)classDescStack.pop()) != null);
  915.         } finally {
  916.         classDescStack.setSize(stackMark);
  917.         }
  918.     }
  919.     }
  920.  
  921.     /*
  922.      * Return a replacement for 'forObject', if one exists.
  923.      */
  924.     private Object lookupReplace(Object obj) {
  925.     for (int i = 0; i < nextReplaceOffset; i+= 2) {
  926.         if (replaceObjects[i] == obj)
  927.         return replaceObjects[i+1];
  928.     }
  929.     return obj;
  930.     }
  931.  
  932.  
  933.     /* Serialize the reference if it is NULL or is for an object that
  934.      * was already replaced or already serialized.
  935.      * If the object was already replaced, look for the replacement
  936.      * object in the known objects and if found, write its handle
  937.      * @param checkForReplace  only if true. Enables optimization of
  938.      *                         not checking for replacement of non-replacable objects.
  939.      * @returns True if the reference is either null or a repeat.
  940.      */
  941.     private boolean serializeNullAndRepeat(Object obj, boolean checkForReplace)
  942.     throws IOException
  943.     {
  944.         if (obj == null) {
  945.         writeCode(TC_NULL);
  946.         return true;
  947.     }
  948.  
  949.     /* Look to see if this object has already been replaced.
  950.      * If so, proceed using the replacement object.
  951.      */
  952.     if (checkForReplace && replaceObjects != null) {
  953.         obj = lookupReplace(obj);
  954.     }
  955.  
  956.     int handle = findWireOffset(obj);
  957.     if (handle >= 0) {
  958.         /* Add a reference to the stream */
  959.         writeCode(TC_REFERENCE);
  960.         writeInt(handle + baseWireHandle);
  961.         return true;
  962.     }
  963.     return false;        // not serialized, its up to the caller
  964.     }
  965.  
  966.     /*
  967.      * Locate and return if found the handle for the specified object.
  968.      * -1 is returned if the object does not occur in the array of
  969.      * known objects.
  970.      */
  971.     private int findWireOffset(Object obj) {
  972.     int hash = System.identityHashCode(obj);
  973.     int index = (hash & 0x7FFFFFFF) % wireHash2Handle.length;
  974.  
  975.     for (int handle = wireHash2Handle[index];
  976.          handle >= 0;
  977.          handle = wireNextHandle[handle]) {
  978.  
  979.         if (wireHandle2Object.get(handle) == obj)
  980.         return handle;
  981.     }
  982.     return -1;
  983.     }
  984.  
  985.     /* Allocate a handle for an object.
  986.      * The Vector is indexed by the wireHandleOffset
  987.      * and contains the object.
  988.      * Allow caller to specify the hash method for the object.
  989.      */
  990.     private void assignWireOffset(Object obj)
  991.     throws IOException
  992.     {
  993.     if (nextWireOffset == wireNextHandle.length) {
  994.         int[] oldnexthandles = wireNextHandle;
  995.         wireNextHandle = new int[nextWireOffset*2];
  996.         System.arraycopy(oldnexthandles, 0,
  997.                  wireNextHandle, 0,
  998.                  nextWireOffset);
  999.     }
  1000.     if (nextWireOffset >= wireHashCapacity) {
  1001.         growWireHash2Handle();
  1002.     }
  1003.     wireHandle2Object.add(obj);
  1004.     hashInsert(obj, nextWireOffset);
  1005.     nextWireOffset++;
  1006.     return;
  1007.     }
  1008.  
  1009.     private void growWireHash2Handle() {
  1010.     // double hash table spine.
  1011.     wireHashSizePower++;
  1012.     wireHash2Handle = new int[(1 << wireHashSizePower) - 1];
  1013.     Arrays.fill(wireHash2Handle, -1);
  1014.  
  1015.     for (int i = 0; i < nextWireOffset; i++) {
  1016.         wireNextHandle[i] = 0;
  1017.     }
  1018.  
  1019.     // refill hash table.
  1020.     for (int i = 0; i < wireHandle2Object.size(); i++) {
  1021.         hashInsert(wireHandle2Object.get(i), i);
  1022.     }
  1023.  
  1024.     wireHashCapacity = (1 << wireHashSizePower) * wireHashLoadFactor;
  1025.     }
  1026.  
  1027.     /*
  1028.      * Insert the specified object into the hash array and link if
  1029.      * necessary. Put the new object into the hash table and link the
  1030.      * previous to it. Newer objects occur earlier in the list.
  1031.      */
  1032.     private void hashInsert(Object obj, int offset) {
  1033.     int hash = System.identityHashCode(obj);
  1034.     int index = (hash & 0x7FFFFFFF) % wireHash2Handle.length;
  1035.     wireNextHandle[offset] = wireHash2Handle[index];
  1036.     wireHash2Handle[index] = offset;
  1037.     }
  1038.  
  1039.     /*
  1040.      * Add a replacement object to the table.
  1041.      * The even numbered indices are the original objects.
  1042.      * The odd numbered indices are the replacement objects.
  1043.      *
  1044.      */
  1045.     private void addReplacement(Object orig, Object replacement) {
  1046.     // Extend the array if there isn't room for this new element
  1047.  
  1048.     if (replaceObjects == null) {
  1049.         replaceObjects = new Object[10];
  1050.     }
  1051.     if (nextReplaceOffset == replaceObjects.length) {
  1052.         Object[] oldhandles = replaceObjects;
  1053.         replaceObjects = new Object[2+nextReplaceOffset*2];
  1054.         System.arraycopy(oldhandles, 0,
  1055.                  replaceObjects, 0,
  1056.                  nextReplaceOffset);
  1057.     }
  1058.     replaceObjects[nextReplaceOffset++] = orig;
  1059.     replaceObjects[nextReplaceOffset++] = replacement;
  1060.     }
  1061.  
  1062.     /* Write out the code indicating the type what follows.
  1063.      * See ObjectStreamConstants for definitions.
  1064.      */
  1065.     private void writeCode(int tag)
  1066.     throws IOException
  1067.     {
  1068.     writeByte(tag);
  1069.     }
  1070.  
  1071.     /*
  1072.      * Implement the OutputStream methods.  The stream has
  1073.      * two modes used internally to ObjectOutputStream.  When
  1074.      * in BlockData mode, all writes are buffered and written
  1075.      * to the underlying stream prefixed by a code and length.
  1076.      * When not in BlockData mode (false), writes pass directly
  1077.      * through to the underlying stream.
  1078.      * The BlockData mode is used to encapsulate data written
  1079.      * by class specific writeObject methods that is intended
  1080.      * only to be read by corresponding readObject method of the
  1081.      * same class.  The blocking of data allows it to be skipped
  1082.      * if necessary.
  1083.      *
  1084.      * The setBlockData method is used to switch buffering
  1085.      * on and off.  When switching between on and off
  1086.      * the buffer is drained.
  1087.      *
  1088.      * The actual buffering is very similar to that of
  1089.      * BufferedOutputStream but BufferedOutputStream can
  1090.      * write to the underlying stream without the headers.
  1091.      */
  1092.     private boolean blockDataMode;    /* true to buffer and block data */
  1093.     private byte[] buf;        /* byte array of buffered data. */
  1094.     private int count;        /* count of bytes in the buffer */
  1095.     private OutputStream out;    /* Stream to write the data to */
  1096.  
  1097.     /**
  1098.      * Writes a byte. This method will block until the byte is actually
  1099.      * written.
  1100.      * @param b    the byte
  1101.      * @exception IOException If an I/O error has occurred.
  1102.      */
  1103.     public void write(int data) throws IOException {
  1104.  
  1105.     if (count >= buf.length)
  1106.         drain();        /* Drain, make room for more */
  1107.     buf[count++] = (byte)data;
  1108.     }
  1109.  
  1110.     /**
  1111.      * Writes an array of bytes. This method will block until the bytes
  1112.      * are actually written.
  1113.      * @param b    the data to be written
  1114.      * @exception IOException If an I/O error has occurred.
  1115.      */
  1116.     public void write(byte b[]) throws IOException {
  1117.     write(b, 0, b.length);
  1118.     }
  1119.  
  1120.     /*
  1121.      * Writes a sub array of bytes.
  1122.      * @param b    the data to be written
  1123.      * @param off    the start offset in the data
  1124.      * @param len    the number of bytes that are written
  1125.      * @param copyOnWrite do not expose b to overrides of ObjectStream.write,
  1126.      *                    copy the contents of b to a buffer before writing.
  1127.      * @exception IOException If an I/O error has occurred.
  1128.      */
  1129.     private void writeInternal(byte b[], int off, int len,
  1130.                    boolean copyOnWrite) throws IOException {
  1131.     if (len < 0) {
  1132.         throw new NullPointerException();
  1133.     } else if ((off < 0) || (off > b.length) || (len < 0) ||
  1134.            ((off + len) > b.length) || ((off + len) < 0)) {
  1135.         throw new IndexOutOfBoundsException();
  1136.     } else if (len == 0) {
  1137.         return;
  1138.     }
  1139.  
  1140.     if (blockDataMode) {
  1141.         writeCanonical(b, off, len);
  1142.     } else {
  1143.         /*
  1144.          * If array will fit in output buffer, copy it in there; otherwise,
  1145.          * drain anything in the buffer and send it through to underlying
  1146.          * output stream directly.
  1147.          */
  1148.         int avail = buf.length - count;
  1149.         if (len <= avail) {
  1150.         System.arraycopy(b, off, buf, count, len);
  1151.         count += len;
  1152.         } else if (copyOnWrite) {
  1153.         bufferedWrite(b, off, len);
  1154.         } else {
  1155.         drain();
  1156.         out.write(b, off, len);
  1157.         }
  1158.     }
  1159.     }
  1160.  
  1161.     /**
  1162.      * Writes a sub array of bytes.
  1163.      * @param b    the data to be written
  1164.      * @param off    the start offset in the data
  1165.      * @param len    the number of bytes that are written
  1166.      * @exception IOException If an I/O error has occurred.
  1167.      */
  1168.     public void write(byte b[], int off, int len) throws IOException {
  1169.     writeInternal(b, off, len, false);
  1170.     }
  1171.  
  1172.     /* Use write buffering of byte[] b to prevent exposure of
  1173.      * 'b' reference to untrusted overrides of ObjectOutput.write(byte[]).
  1174.      *
  1175.      * NOTE: Method is only intended for protecting serializable byte []
  1176.      *       fields written by default serialization. Thus, it can
  1177.      *       never get called while in blockDataMode.
  1178.      */
  1179.     private void bufferedWrite(byte b[], int off, int len) throws IOException {
  1180.     int bufAvail = buf.length - count;
  1181.     int bytesToWrite = len;
  1182.  
  1183.     // Handle case where byte array is larger than available buffer.
  1184.     if (bytesToWrite > bufAvail) {
  1185.  
  1186.         // Logically: fill rest of 'buf' with 'b' and drain.
  1187.         System.arraycopy(b, off, buf, count, bufAvail);
  1188.         off += bufAvail;
  1189.         bytesToWrite -= bufAvail;
  1190.         out.write(buf, 0, buf.length);
  1191.         count = 0;
  1192.  
  1193.         // Write out buf.length chunks of byte array.
  1194.         while (bytesToWrite >= buf.length) {
  1195.         System.arraycopy(b, off, buf, 0, buf.length);
  1196.         out.write(buf, 0, buf.length);
  1197.         off += buf.length;
  1198.         bytesToWrite -= buf.length;
  1199.  
  1200.         // Optimization: do not modify or access "count" in this loop.
  1201.         }
  1202.     }
  1203.  
  1204.     // Put remainder of byte array b into buffer.
  1205.     if (bytesToWrite != 0) {
  1206.         System.arraycopy(b, off, buf, count, bytesToWrite);
  1207.         count += bytesToWrite;
  1208.     }
  1209.     }
  1210.  
  1211.     /**
  1212.      * Flushes the stream. This will write any buffered
  1213.      * output bytes and flush through to the underlying stream.
  1214.      * @exception IOException If an I/O error has occurred.
  1215.      */
  1216.     public void flush() throws IOException {
  1217.     drain();
  1218.     out.flush();
  1219.     }
  1220.  
  1221.     /**
  1222.      * Drain any buffered data in ObjectOutputStream.  Similar to flush
  1223.      * but does not propagate the flush to the underlaying stream.
  1224.      */
  1225.     protected void drain() throws IOException {
  1226.     /*
  1227.      * Drain the data buffer.
  1228.      * If the blocking mode is on, prepend the buffer
  1229.      * with the code for a blocked data and the length.
  1230.      * The code is TC_BLOCKDATA and the length is < 255 so it fits
  1231.      * in a byte.
  1232.      */
  1233.     if (count == 0)
  1234.         return;
  1235.  
  1236.     if (blockDataMode)
  1237.         writeBlockDataHeader(count);
  1238.     out.write(buf, 0, count);
  1239.     count = 0;
  1240.     }
  1241.  
  1242.     /**
  1243.      * Closes the stream. This method must be called
  1244.      * to release any resources associated with the
  1245.      * stream.
  1246.      * @exception IOException If an I/O error has occurred.
  1247.      */
  1248.     public void close() throws IOException {
  1249.     flush();        /* Make sure we're not holding any data */
  1250.     out.close();
  1251.     }
  1252.  
  1253.     /*
  1254.      * Set the blockData mode,  if it turned from on to off
  1255.      * the buffer is drained.  The previous mode is returned.
  1256.      */
  1257.     private boolean setBlockData(boolean mode) throws IOException {
  1258.     if (blockDataMode == mode)
  1259.         return mode;
  1260.     drain();
  1261.     blockDataMode = mode;
  1262.     return !mode;        /* previous value was the opposite */
  1263.     }
  1264.  
  1265.     /* Write the Block-data marker and the length of the data to follow
  1266.      * to stream 'out'. If the length is < 256, use the short header form.
  1267.      * othewise use the long form.
  1268.      */
  1269.     private void writeBlockDataHeader(int len) throws IOException {
  1270.     if (len <= 255) {
  1271.         out.write(TC_BLOCKDATA);
  1272.         out.write((byte)len);
  1273.     } else {
  1274.         // use block data with int size if necessary
  1275.         out.write(TC_BLOCKDATALONG);
  1276.         // send 32 bit int directly to underlying stream
  1277.         out.write((byte)((len >> 24) & 0xFF));
  1278.         out.write((byte)((len >> 16) & 0xFF));
  1279.         out.write((byte)((len >>  8) & 0xFF));
  1280.         out.write((byte)(len & 0xFF));
  1281.     }
  1282.     }
  1283.  
  1284.     /* Canonical form requires constant blocking factor. Write data
  1285.      * in b array in constant block-data chunks.
  1286.      *
  1287.      * Assumes only called when blockDataMode is true.
  1288.      */
  1289.     private void writeCanonical(byte b[], int off, int len)
  1290.     throws IOException
  1291.     {
  1292.     int bufAvail = buf.length - count;
  1293.     int bytesToWrite = len;
  1294.  
  1295.     // Handle case where byte array is larger than available buffer.
  1296.     if (bytesToWrite > bufAvail) {
  1297.         // Logically: fill rest of 'buf' with 'b' and drain.
  1298.         // Optimization: avoid copying to 'buf', write partial 'buf' and partial
  1299.         //               'b' directly to 'out'.
  1300.         writeBlockDataHeader(buf.length);
  1301.         out.write(buf, 0, count);
  1302.         out.write(b, off, bufAvail);
  1303.  
  1304.         count = 0;
  1305.         off += bufAvail;
  1306.         bytesToWrite -= bufAvail;
  1307.  
  1308.         // Optimization: write 'buf.length' BlockData directly to stream.
  1309.         while (bytesToWrite >= buf.length) {
  1310.         if (blockDataMode)
  1311.             writeBlockDataHeader(buf.length);
  1312.         out.write(b, off, buf.length);
  1313.         off += buf.length;
  1314.         bytesToWrite -= buf.length;
  1315.         }
  1316.     }
  1317.  
  1318.     // Put remainder of byte array into buffer.
  1319.     if (bytesToWrite != 0) {
  1320.         System.arraycopy(b, off, buf, count, bytesToWrite);
  1321.         count += bytesToWrite;
  1322.     }
  1323.     }
  1324.  
  1325.     /* -------------------------------------------------------------- */
  1326.     /*
  1327.      * Provide the methods to implement DataOutput.
  1328.      * These are copied from DataOutputStream to avoid the overhead
  1329.      * of multiple method calls and to buffer the data directly.
  1330.      */
  1331.     private DataOutputStream dos;
  1332.  
  1333.     /**
  1334.      * Writes a boolean.
  1335.      * @param data the boolean to be written
  1336.      */
  1337.     public void writeBoolean(boolean data) throws IOException {
  1338.     if (count >= buf.length)
  1339.         drain();
  1340.     buf[count++] = (byte)(data ? 1 : 0);
  1341.  
  1342.     }
  1343.  
  1344.     /**
  1345.      * Writes an 8 bit byte.
  1346.      * @param data the byte value to be written
  1347.      */
  1348.     public void writeByte(int data) throws IOException  {
  1349.     if (count >= buf.length)
  1350.         drain();
  1351.     buf[count++] = (byte)data;
  1352.     }
  1353.  
  1354.     /**
  1355.      * Writes a 16 bit short.
  1356.      * @param data the short value to be written
  1357.      */
  1358.     public void writeShort(int data)  throws IOException {
  1359.     if (count + 2 > buf.length) {
  1360.         if (blockDataMode) {
  1361.  
  1362.         // normalize block-data record by writing a byte at a time.
  1363.         dos.writeShort(data);
  1364.         return;
  1365.         } else {
  1366.         drain();
  1367.         }
  1368.     }
  1369.     buf[count++] = (byte)((data >>>  8));
  1370.     buf[count++] = (byte)((data >>>  0));
  1371.     }
  1372.  
  1373.     /**
  1374.      * Writes a 16 bit char.
  1375.      * @param data the char value to be written
  1376.      */
  1377.     public void writeChar(int data)  throws IOException {
  1378.     if (count + 2 > buf.length) {
  1379.         if (blockDataMode) {
  1380.  
  1381.         // normalize block-data record by writing a byte at a time.
  1382.         dos.writeChar(data);
  1383.         return;
  1384.         } else {
  1385.         drain();
  1386.         }
  1387.     }
  1388.     buf[count++] = (byte)((data >>>  8));
  1389.     buf[count++] = (byte)((data >>>  0));
  1390.     }
  1391.  
  1392.     /**
  1393.      * Writes a 32 bit int.
  1394.      * @param data the integer value to be written
  1395.      */
  1396.     public void writeInt(int data)  throws IOException {
  1397.     if (count + 4 > buf.length) {
  1398.         if (blockDataMode) {
  1399.  
  1400.         // normalize block-data record by writing a byte at a time.
  1401.         dos.writeInt(data);
  1402.         return;
  1403.         } else {
  1404.         drain();
  1405.         }
  1406.     }
  1407.     buf[count++] = (byte)((data >>> 24));
  1408.     buf[count++] = (byte)((data >>> 16));
  1409.     buf[count++] = (byte)((data >>>  8));
  1410.     buf[count++] = (byte)((data >>>  0));
  1411.     }
  1412.  
  1413.     /**
  1414.      * Writes a 64 bit long.
  1415.      * @param data the long value to be written
  1416.      */
  1417.     public void writeLong(long data)  throws IOException {
  1418.     if (count + 8 > buf.length) {
  1419.         if (blockDataMode) {
  1420.  
  1421.         // normalize block-data record by writing a byte at a time.
  1422.         dos.writeLong(data);
  1423.         return;
  1424.         } else {
  1425.         drain();
  1426.         }
  1427.     }
  1428.     buf[count++] = (byte)((int)(data >>> 56));
  1429.     buf[count++] = (byte)((int)(data >>> 48));
  1430.     buf[count++] = (byte)((int)(data >>> 40));
  1431.     buf[count++] = (byte)((int)(data >>> 32));
  1432.     buf[count++] = (byte)((data >>> 24));
  1433.     buf[count++] = (byte)((data >>> 16));
  1434.     buf[count++] = (byte)((data >>>  8));
  1435.     buf[count++] = (byte)((data >>>  0));
  1436.     }
  1437.  
  1438.     /**
  1439.      * Writes a 32 bit float.
  1440.      * @param data the float value to be written
  1441.      */
  1442.     public void writeFloat(float data) throws IOException {
  1443.     int value = Float.floatToIntBits(data);
  1444.     if (count + 4 > buf.length) {
  1445.         if (blockDataMode) {
  1446.  
  1447.         // normalize block-data record by writing a byte at a time.
  1448.         dos.writeFloat(data);
  1449.         return;
  1450.         } else {
  1451.         drain();
  1452.         }
  1453.     }
  1454.     buf[count++] = (byte)((value >>> 24));
  1455.     buf[count++] = (byte)((value >>> 16));
  1456.     buf[count++] = (byte)((value >>>  8));
  1457.     buf[count++] = (byte)((value >>>  0));
  1458.     }
  1459.  
  1460.     /**
  1461.      * Writes a 64 bit double.
  1462.      * @param data the double value to be written
  1463.      */
  1464.     public void writeDouble(double data) throws IOException {
  1465.     long value = Double.doubleToLongBits(data);
  1466.     if (count + 8 > buf.length) {
  1467.         if (blockDataMode) {
  1468.  
  1469.         // normalize block-data record by writing a byte at a time.
  1470.         dos.writeDouble(data);
  1471.         return;
  1472.         } else {
  1473.         drain();
  1474.         }
  1475.     }
  1476.     buf[count++] = (byte)((int)(value >>> 56));
  1477.     buf[count++] = (byte)((int)(value >>> 48));
  1478.     buf[count++] = (byte)((int)(value >>> 40));
  1479.     buf[count++] = (byte)((int)(value >>> 32));
  1480.     buf[count++] = (byte)((value >>> 24));
  1481.     buf[count++] = (byte)((value >>> 16));
  1482.     buf[count++] = (byte)((value >>>  8));
  1483.     buf[count++] = (byte)((value >>>  0));
  1484.     }
  1485.  
  1486.     /**
  1487.      * Writes a String as a sequence of bytes.
  1488.      * @param s the String of bytes to be written
  1489.      */
  1490.     public void writeBytes(String data) throws IOException {
  1491.     dos.writeBytes(data);
  1492.     }
  1493.  
  1494.     /**
  1495.      * Writes a String as a sequence of chars.
  1496.      * @param s the String of chars to be written
  1497.      */
  1498.     public void writeChars(String data) throws IOException {
  1499.     dos.writeChars(data);
  1500.     }
  1501.  
  1502.     /**
  1503.      * Primitive data write of this String in UTF format.
  1504.      *
  1505.      * Note that there is a significant difference between
  1506.      * writing a String into the stream as primitive data or
  1507.      * as an Object. A String instance written by writeObject
  1508.      * is written into the stream as a String initially. Future
  1509.      * writeObject() calls write references to the string into
  1510.      * the stream.
  1511.      *
  1512.      * @param str the String in UTF format
  1513.      */
  1514.     public void writeUTF(String data) throws IOException {
  1515.     dos.writeUTF(data);
  1516.     }
  1517.  
  1518.     /* Write the fields of the specified class by invoking the appropriate
  1519.      * write* method on this class.
  1520.      */
  1521.     private void outputClassFields(Object o, Class cl,
  1522.                    ObjectStreamField[] fields)
  1523.     throws IOException, InvalidClassException {
  1524.  
  1525.     for (int i = 0; i < fields.length; i++) {
  1526.         if (fields[i].getField() == null)
  1527.         throw new InvalidClassException(cl.getName(),
  1528.             "Nonexistent field " + fields[i].getName());
  1529.  
  1530.         try {
  1531.         switch (fields[i].getTypeCode()) {
  1532.         case 'B':
  1533.             byte byteValue = fields[i].getField().getByte(o);
  1534.             writeByte(byteValue);
  1535.                 break;
  1536.         case 'C':
  1537.             char charValue = fields[i].getField().getChar(o);
  1538.             writeChar(charValue);
  1539.                 break;
  1540.         case 'F':
  1541.             float floatValue = fields[i].getField().getFloat(o);
  1542.             writeFloat(floatValue);
  1543.                 break;
  1544.         case 'D' :
  1545.             double doubleValue = fields[i].getField().getDouble(o);
  1546.             writeDouble(doubleValue);
  1547.             break;
  1548.         case 'I':
  1549.             int intValue = fields[i].getField().getInt(o);
  1550.             writeInt(intValue);
  1551.                 break;
  1552.         case 'J':
  1553.             long longValue = fields[i].getField().getLong(o);
  1554.             writeLong(longValue);
  1555.             break;
  1556.         case 'S':
  1557.             short shortValue = fields[i].getField().getShort(o);
  1558.             writeShort(shortValue);
  1559.                 break;
  1560.         case 'Z':
  1561.             boolean booleanValue = fields[i].getField().getBoolean(o);
  1562.             writeBoolean(booleanValue);
  1563.                 break;
  1564.         case '[':
  1565.         case 'L':
  1566.             Object objectValue = fields[i].getField().get(o);
  1567.             writeObject(objectValue);
  1568.                 break;
  1569.         default:
  1570.             throw new InvalidClassException(cl.getName());
  1571.         }
  1572.         } catch (IllegalAccessException e) {
  1573.         throw new InvalidClassException(cl.getName(), e.getMessage());
  1574.         }
  1575.     }
  1576.     }
  1577.  
  1578.     /*
  1579.      * Test if WriteObject method is present, and if so, invoke writer.
  1580.      */
  1581.     private void invokeObjectWriter(Object obj)
  1582.     throws IOException
  1583.     {
  1584.     try {
  1585.         currentClassDesc.writeObjectMethod.invoke(obj, writeObjectArglist);
  1586.     } catch (InvocationTargetException e) {
  1587.         Throwable t = e.getTargetException();
  1588.         if (t instanceof IOException)
  1589.         throw (IOException)t;
  1590.         else if (t instanceof RuntimeException)
  1591.         throw (RuntimeException) t;
  1592.         else if (t instanceof Error)
  1593.         throw (Error) t;
  1594.         else
  1595.         throw new Error("interal error");
  1596.     } catch (IllegalAccessException e) {
  1597.         // cannot happen
  1598.     }
  1599.     }
  1600.  
  1601.     /*************************************/
  1602.  
  1603.     /**
  1604.      * Provide programatic access to the persistent fields to be written
  1605.      * to ObjectOutput.
  1606.      *
  1607.      * @since JDK 1.2
  1608.      */
  1609.     static public abstract class PutField {
  1610.     /**
  1611.      * Put the value of the named boolean field into the persistent field.
  1612.      */
  1613.     abstract public void put(String name, boolean value);
  1614.  
  1615.     /**
  1616.      * Put the value of the named char field into the persistent fields.
  1617.      */
  1618.     abstract public void put(String name, char value);
  1619.  
  1620.     /**
  1621.      * Put the value of the named byte field into the persistent fields.
  1622.      */
  1623.     abstract public void put(String name, byte value);
  1624.  
  1625.     /**
  1626.      * Put the value of the named short field into the persistent fields.
  1627.      */
  1628.     abstract public void put(String name, short value);
  1629.  
  1630.     /**
  1631.      * Put the value of the named int field into the persistent fields.
  1632.      */
  1633.     abstract public void put(String name, int value);
  1634.  
  1635.     /**
  1636.      * Put the value of the named long field into the persistent fields.
  1637.      */
  1638.     abstract public void put(String name, long value);
  1639.  
  1640.     /**
  1641.      * Put the value of the named float field into the persistent fields.
  1642.      */
  1643.     abstract public void put(String name, float value);
  1644.  
  1645.     /**
  1646.      * Put the value of the named double field into the persistent field.
  1647.      */
  1648.     abstract public void put(String name, double value);
  1649.  
  1650.     /**
  1651.      * Put the value of the named Object field into the persistent field.
  1652.      */
  1653.     abstract public void put(String name, Object value);
  1654.  
  1655.     /**
  1656.      * Write the data and fields to the specified ObjectOutput stream.
  1657.      */
  1658.     abstract public void write(ObjectOutput out) throws IOException;
  1659.     };
  1660.  
  1661.     /*************************************************************/
  1662.  
  1663.  
  1664.     /**
  1665.      * Provide access to the persistent fields to be written to the output stream.
  1666.      */
  1667.     static final class PutFieldImpl extends PutField {
  1668.      /**
  1669.       * Put the value of the named boolean field into the persistent field.
  1670.       */
  1671.      public void put(String name, boolean value)
  1672.          throws IllegalArgumentException
  1673.      {
  1674.          ObjectStreamField field = desc.getField(name, Boolean.TYPE);
  1675.          if (field == null || field.getType() != Boolean.TYPE)
  1676.          throw new IllegalArgumentException("No such boolean field");
  1677.          data[field.getOffset()] = (byte)(value ? 1 : 0);
  1678.      }
  1679.  
  1680.      /**
  1681.       * Put the value of the named char field into the persistent fields.
  1682.       */
  1683.      public void put(String name, char value) {
  1684.          ObjectStreamField field = desc.getField(name, Character.TYPE);
  1685.          if (field == null || field.getType() != Character.TYPE)
  1686.          throw new IllegalArgumentException("No such char field");
  1687.          data[field.getOffset()] = (byte)(value >> 8);
  1688.          data[field.getOffset()+1] = (byte)(value);
  1689.      }
  1690.  
  1691.      /**
  1692.       * Put the value of the named byte field into the persistent fields.
  1693.       */
  1694.      public void put(String name, byte value) {
  1695.          ObjectStreamField field = desc.getField(name, Byte.TYPE);
  1696.          if (field == null || field.getType() != Byte.TYPE)
  1697.          throw new IllegalArgumentException("No such byte field");
  1698.          data[field.getOffset()] = value;
  1699.      }
  1700.  
  1701.      /**
  1702.       * Put the value of the named short field into the persistent fields.
  1703.       */
  1704.      public void put(String name, short value) {
  1705.          ObjectStreamField field = desc.getField(name, Short.TYPE);
  1706.          if (field == null || field.getType() != Short.TYPE)
  1707.          throw new IllegalArgumentException("No such short field");
  1708.  
  1709.          int loffset = field.getOffset();
  1710.          data[loffset] = (byte)(value >> 8);
  1711.          data[loffset+1] = (byte)(value);
  1712.      }
  1713.  
  1714.      /**
  1715.       * Put the value of the named int field into the persistent fields.
  1716.       */
  1717.      public void put(String name, int value) {
  1718.          ObjectStreamField field = desc.getField(name, Integer.TYPE);
  1719.          if (field == null || field.getType() != Integer.TYPE)
  1720.          throw new IllegalArgumentException("No such int field");
  1721.  
  1722.          int loffset = field.getOffset();
  1723.          data[loffset] = (byte)(value >> 24);
  1724.          data[loffset+1] = (byte)(value >> 16);
  1725.          data[loffset+2] = (byte)(value >> 8);
  1726.          data[loffset+3] = (byte)value;
  1727.      }
  1728.  
  1729.      /**
  1730.       * Put the value of the named long field into the persistent fields.
  1731.       */
  1732.      public void put(String name, long value) {
  1733.          ObjectStreamField field = desc.getField(name, Long.TYPE);
  1734.          if (field == null || field.getType() != Long.TYPE)
  1735.          throw new IllegalArgumentException("No such long field");
  1736.  
  1737.          int loffset = field.getOffset();
  1738.          data[loffset] = (byte)(value >> 56);
  1739.          data[loffset+1] = (byte)(value >> 48);
  1740.          data[loffset+2] = (byte)(value >> 40);
  1741.          data[loffset+3] = (byte)(value >> 32);
  1742.          data[loffset+4] = (byte)(value >> 24);
  1743.          data[loffset+5] = (byte)(value >> 16);
  1744.          data[loffset+6] = (byte)(value >> 8);
  1745.          data[loffset+7] = (byte)value;
  1746.      }
  1747.  
  1748.      /**
  1749.       * Put the value of the named float field into the persistent fields.
  1750.       */
  1751.      public void put(String name, float value) {
  1752.          int val = Float.floatToIntBits(value);
  1753.          ObjectStreamField field = desc.getField(name, Float.TYPE);
  1754.          if (field == null || field.getType() != Float.TYPE)
  1755.          throw new IllegalArgumentException("No such float field");
  1756.  
  1757.          int loffset = field.getOffset();
  1758.          data[loffset] = (byte)(val >> 24);
  1759.          data[loffset+1] = (byte)(val >> 16);
  1760.          data[loffset+2] = (byte)(val >> 8);
  1761.          data[loffset+3] = (byte)val;
  1762.      }
  1763.  
  1764.      /**
  1765.          * Put the value of the named double field into the persistent field.
  1766.          */
  1767.      public void put(String name, double value) {
  1768.          long val = Double.doubleToLongBits(value);
  1769.          ObjectStreamField field = desc.getField(name, Double.TYPE);
  1770.          if (field == null || field.getType() != Double.TYPE)
  1771.          throw new IllegalArgumentException("No such double field");
  1772.  
  1773.          int loffset = field.getOffset();
  1774.          data[loffset] = (byte)(val >> 56);
  1775.          data[loffset+1] = (byte)(val >> 48);
  1776.          data[loffset+2] = (byte)(val >> 40);
  1777.          data[loffset+3] = (byte)(val >> 32);
  1778.          data[loffset+4] = (byte)(val >> 24);
  1779.          data[loffset+5] = (byte)(val >> 16);
  1780.          data[loffset+6] = (byte)(val >> 8);
  1781.          data[loffset+7] = (byte)val;
  1782.      }
  1783.  
  1784.      /**
  1785.       * Put the value of the named Object field into the persistent field.
  1786.       */
  1787.      public void put(String name, Object value) {
  1788.          ObjectStreamField field = desc.getField(name, Object.class);
  1789.          if (field == null || field.isPrimitive())
  1790.          throw new IllegalArgumentException("No such object field");
  1791.          objects[field.getOffset()] = value;
  1792.      }
  1793.  
  1794.      /**
  1795.       * Write the data and fields to the specified stream.
  1796.       */
  1797.      public void write(ObjectOutput out) throws IOException {
  1798.          if (data != null)
  1799.          out.write(data, 0, data.length);
  1800.  
  1801.          if (objects != null) {
  1802.          for (int i = 0; i < objects.length; i++)
  1803.              out.writeObject(objects[i]);
  1804.          }
  1805.      }
  1806.  
  1807.      /**
  1808.       * Create a PutField object for the a Class.
  1809.       * Allocate the arrays for primitives and objects.
  1810.       */
  1811.      PutFieldImpl(ObjectStreamClass descriptor) {
  1812.          desc = descriptor;
  1813.          if (desc.primBytes > 0)
  1814.          data = new byte[desc.primBytes];
  1815.          if (desc.objFields > 0)
  1816.          objects = new Object[desc.objFields];
  1817.      }
  1818.  
  1819.     /*
  1820.      * The byte array that contains the bytes for the primitive fields.
  1821.      * The Object array that contains the objects for the object fields.
  1822.      */
  1823.      private byte[] data;
  1824.      private Object[] objects;
  1825.      private ObjectStreamClass desc;
  1826.      };
  1827.  
  1828.     /*************************************************************/
  1829.  
  1830.     /* Remember the first exception that stopped this stream. */
  1831.     private IOException abortIOException = null;
  1832.  
  1833.     /* Object references are mapped to the wire handles through a hashtable
  1834.      * WireHandles are integers generated by the ObjectOutputStream,
  1835.      * they need only be unique within a stream.
  1836.      * Objects are assigned sequential handles and stored in wireHandle2Object.
  1837.      * The handle for an object is its index in wireHandle2Object.
  1838.      * Object with the "same" hashcode are chained using wireHash2Handle.
  1839.      * The hashcode of objects is used to index through the wireHash2Handle.
  1840.      * -1 is the marker for unused cells in wireNextHandle
  1841.      */
  1842.     private ArrayList wireHandle2Object;
  1843.     private int nextWireOffset;
  1844.  
  1845.     /* the next five members implement an inline hashtable. */
  1846.     private int[] wireHash2Handle;        // hash spine
  1847.     private int[] wireNextHandle;         // next hash bucket entry
  1848.     private int   wireHashSizePower = 2;  // current power of 2 hash table size - 1
  1849.     private int   wireHashLoadFactor = 7; // avg number of elements per bucket
  1850.     private int   wireHashCapacity = (1 << wireHashSizePower) * wireHashLoadFactor;
  1851.  
  1852.     /* The object is the current object and ClassDescriptor is the current
  1853.      * subclass of the object being written. Nesting information is kept
  1854.      * on the stack.
  1855.      */
  1856.     private Object currentObject;
  1857.     private ObjectStreamClass currentClassDesc;
  1858.     private Stack classDescStack;
  1859.     private PutField currentPutFields;
  1860.     private Object[] writeObjectArglist = {this};
  1861.  
  1862.     /*
  1863.      * Flag set to true to allow replaceObject to be called.
  1864.      * Set by enableReplaceObject.
  1865.      * The array of replaceObjects and the index of the next insertion.
  1866.      */
  1867.     boolean enableReplace;
  1868.     private Object[] replaceObjects;
  1869.     private int nextReplaceOffset;
  1870.     static final private boolean REPLACEABLE = true;
  1871.     static final private boolean NOT_REPLACEABLE = false;
  1872.  
  1873.     /* Recursion level, starts at zero and is incremented for each entry
  1874.      * to writeObject.  Decremented before exit.
  1875.      */
  1876.     private int recursionDepth = 0;
  1877.  
  1878.     /* If true, use JDK 1.1 Externalizable data format. */
  1879.     boolean useDeprecatedExternalizableFormat = false;
  1880.  
  1881.     /* if true, override writeObject implementation with writeObjectOverride.
  1882.      */
  1883.     private boolean enableSubclassImplementation;
  1884.  
  1885.     /**
  1886.      * Unsynchronized Stack.
  1887.      */
  1888.     static final class Stack extends ArrayList {
  1889.     void setSize(int newSize) {
  1890.         if (newSize == 0) {
  1891.         clear();
  1892.         } else {
  1893.         int len = size();
  1894.  
  1895.         for (int i = len - 1; i >= newSize; i--) {
  1896.             remove(i);
  1897.         }
  1898.         }
  1899.     }
  1900.  
  1901.     Object push(Object item) {
  1902.         add(item);
  1903.         return item;
  1904.     }
  1905.  
  1906.     /**
  1907.      * Removes the object at the top of this stack and returns that
  1908.      * object as the value of this function.
  1909.      *
  1910.      * @return     The object at the top of this stack (the last item
  1911.      *             of the <tt>Vector</tt> object).
  1912.      * @exception  EmptyStackException  if this stack is empty.
  1913.      */
  1914.     Object pop() {
  1915.         Object    obj;
  1916.         int    len = size();
  1917.  
  1918.         obj = peek();
  1919.         remove(len - 1);
  1920.  
  1921.         return obj;
  1922.     }
  1923.  
  1924.     /**
  1925.      * Looks at the object at the top of this stack without removing it
  1926.      * from the stack.
  1927.      *
  1928.      * @return     the object at the top of this stack (the last item
  1929.      *             of the <tt>Vector</tt> object).
  1930.      * @exception  EmptyStackException  if this stack is empty.
  1931.      */
  1932.     Object peek() {
  1933.         int    len = size();
  1934.  
  1935.         if (len == 0)
  1936.         throw new java.util.EmptyStackException();
  1937.         return get(len - 1);
  1938.     }
  1939.     }
  1940. };
  1941.